package ro.qi.framework.sys.db.pool;

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Vector;

import ro.qi.framework.sys.debug.Logger;

public class JDCConnectionPool {

	private Vector connections;
	private String url, user, password, driverClass;
	private long timeout = 45000;
	private ConnectionReaper reaper;
	private boolean useDB;
	private boolean _debug = false;
	private boolean poolReady = false;

	public JDCConnectionPool(String url, String user, String password, String driverClass, long reaperDelay) {
		if (_debug)
			Logger.info("new JDCConnectionPool(" + driverClass + ")");
		this.url = url;
		this.user = user;
		this.password = password;
		this.driverClass = driverClass;

		connections = new Vector();
		try {
			loadDriver();
		} catch (Throwable e) {
			Logger.error(e.getMessage());
			return;
		}

		//boolean success = this.fillPool();
		//if ( !success ) _logger.error("JDCConnectionPool() FAILED TO FILL POOL");

		reaper = new ConnectionReaper(this, reaperDelay);
		reaper.start();
		poolReady = true;
	}
	
	public void stop() {
		reaper.running = false;
		reaper.interrupt();
	}

	private void loadDriver() throws Exception {
		if (_debug)
			Logger.info("JDCConnectionPool.loadDriver(" + driverClass + ")");
		try {
			Driver driver = (Driver) Class.forName(driverClass).newInstance();
			DriverManager.registerDriver(driver);
			if (_debug)
				Logger.info("Registered JDBC driver " + driverClass);
		} catch (Exception e) {
			Logger.error("Can't register JDBC driver: " + driverClass + ", Exception: " + e);
			throw e;
		}
	}

	public synchronized void reapConnections() {
		if (_debug)
			Logger.info("JDCConnectionPool.reapConnections() size: " + connections.size());

		if (connections == null)
			return;

		int length = connections.size();
		if (length == 0)
			return;

		long stale = System.currentTimeMillis() - timeout;
		for (int i = (length - 1); i >= 0; i--) {

			if (i > (connections.size() - 1))
				continue;

			JDCConnection conn = (JDCConnection) connections.elementAt(i);

			if (conn == null)
				continue;

			if (!conn.validate() || (!conn.inUse() && (stale > conn.getLastUse()))) {
				if (_debug)
					Logger.info("JDCConnectionPool.reapConnections() REMOVE STALE CONNECTION");
				removeConnection(conn);

				/*
				// ADD A NEW ONE
				try {
				  Connection newConn = DriverManager.getConnection(url, user, password);
				  JDCConnection c = new JDCConnection(newConn, this);
				  connections.addElement(c);
				} catch ( Exception e ) {
				  _logger.error("JDCConnectionPool.reapConnections() FAILED TO CREATE A NEW CONNECTION", e );
				}
				*/
			}
		}
	}

	public synchronized void closeConnections() {
		if (_debug)
			Logger.info("JDCConnectionPool.reapConnections()");

		Enumeration connlist = connections.elements();

		while ((connlist != null) && (connlist.hasMoreElements())) {
			JDCConnection conn = (JDCConnection) connlist.nextElement();
			removeConnection(conn);
		}
	}

	private void removeConnection(JDCConnection conn) {
		if (_debug)
			Logger.info("JDCConnectionPool.removeConnection()");

		try {
			long startTime = System.currentTimeMillis();
			conn.close();
			long endTime = System.currentTimeMillis();
			long elapsedTime = endTime - startTime;
			if (_debug)
				Logger.info("JDCConnectionPool.removeConnection() TIME: " + elapsedTime);
		} catch (Exception e) {
			Logger.error("JDCConnectionPool.removeConnection() FAILED ON conn.close() " + e.getMessage());
			connections.removeElement(conn);
			return;
		}

		connections.removeElement(conn);
	}

	public synchronized boolean fillPool(int poolsize) {
		if (_debug)
			Logger.info("JDCConnectionPool.fillPool()");

		while (connections.size() < poolsize) {
			try {
				Connection conn = DriverManager.getConnection(url, user, password);
				JDCConnection c = new JDCConnection(conn, this);
				connections.addElement(c);

			} catch (Exception e) {
				Logger.error("JDCConnectionPool.fillPool() FAILED" + e.getMessage());
				return false;
			}
		}
		return true;
	}

	public synchronized JDCConnection getConnection() throws SQLException {
		if (_debug)
			Logger.info("JDCConnectionPool.getConnection()");

		JDCConnection c;
		for (int i = 0; i < connections.size(); i++) {
			c = (JDCConnection) connections.elementAt(i);
			if (c.lease()) {
				return c;
			}
		}
		if (_debug)
			Logger.info("JDCConnectionPool.getConnection() NO CONNECTIONS AVAILABLE: CREATING NEW CONNECTION");

		long startTime = System.currentTimeMillis();
		Connection conn = DriverManager.getConnection(url, user, password);
		c = new JDCConnection(conn, this);
		c.lease();
		connections.addElement(c);
		long endTime = System.currentTimeMillis();
		//if ( ( endTime - startTime ) > 1000 ) Logger.info("JDCConnectionPool.getConnection() TIME: " + ( endTime - startTime ) );
		if (_debug)
			Logger.info("JDCConnectionPool.getConnection() TIME: " + (endTime - startTime));
		return c;
	}

	public synchronized void returnConnection(JDCConnection conn) {
		if (_debug)
			Logger.info("JDCConnectionPool.returnConnection()");
		conn.expireLease();
	}

	public int getPoolSize() {
		return this.connections.size();
	}
}
